import hashlib
import pyaes
import blowfish
import binascii
from urllib.request import urlopen

from .quality import TrackQuality

BLOWFISH_SECRET = b'jo6aey6haid2Teih'
BLOWFISH_SECRET_STRING = 'g4el58wc0zvf9na1'

def stream(stream, track_data, quality, progress_callback = None):
  quality = quality.value
  
  data = track_data["results"]["DATA"]

  # get track url
  #  - step 1: put a bunch of info together and hash it
  step1 = b'\xa4'.join(map(
    lambda s: s.encode(), [
      data["MD5_ORIGIN"],
      quality,
      data["SNG_ID"],
      data["MEDIA_VERSION"]
    ]
  ))
  hash = hashlib.new("md5")
  hash.update(step1)
  step1_hash = hash.hexdigest()

  #  - step 2: hash + info + add padding
  step2 = str.encode(step1_hash) + b'\xa4' + step1 + b'\xa4'
  while len(step2) % 16 > 0:
    step2 += b'.'

  #  - step 3: AES encryption to get url
  # it will encrypt in parts of 16
  step3 = b''
  aes = pyaes.AESModeOfOperationECB(BLOWFISH_SECRET)
  for index in range(int(len(step2) / 16)):
    block = step2[(index * 16):(index * 16) + 16]
    step3 += binascii.hexlify(aes.encrypt(block))

  #  - step 4: make url
  url = "https://e-cdns-proxy-{}.dzcdn.net/mobile/1/{}".format(
    data["MD5_ORIGIN"][0], # first char of md5origin is cdn to use
    step3.decode('utf-8')
  )

  # get blowfish key
  hash = hashlib.new("md5")
  hash.update(data["SNG_ID"].encode())
  hash = hash.hexdigest()
  key = ""
  for i in range(16):
    key += chr(ord(hash[i]) ^ ord(hash[i+16]) ^ ord(BLOWFISH_SECRET_STRING[i]))

  response = urlopen(url)
  cipher = blowfish.Cipher(key.encode())
  length = int(response.getheader('Content-Length'))
  i = 0
  while True:
    chunk = response.read(2048)
    if progress_callback != None:
      if (i * 2048) > length:
        progress_callback(1)
      else:
        progress_callback((i * 2048) / length)
    if not chunk:
      break
    if (i % 3) == 0 and len(chunk) == 2048:
      chunk = b"".join(
        cipher.decrypt_cbc(chunk, b"\x00\x01\x02\x03\x04\x05\x06\x07")
      )
    stream.write(chunk)
    i += 1
